import request from '@/utils/request';
import {sm4} from 'sm-crypto';

/**
 * @description sm4 接口加密
 * @author hlf
 * @date 2025/3/14 16:12
 **/

let isUrlSafe = true;

let originKey = "1G3U3BMAyqKciPo1_tzbDw==";
let originIv = "MHVhOU1MYTRVc3R6aEdiSg==";


let urlNotSafeKey;
let urlNotSafeIv;

function handleApiEncrypt(config) {
    checkKey();
    // 根据请求传参是 data 还是 params，还有 FormData 形式，进行不同的参数处理方法
    if (config.params) {
        encryptParams(config);
    }
    if (config.data) {
        if (config.headers["Content-Type"] === 'multipart/form-data') {
            encryptFormData(config);
        } else if (config.headers["Content-Type"] === 'application/x-www-form-urlencoded') {
            // 下载文件接口不做处理
            return config;
        } else {
            encryptData(config);
        }
    }
}

function handleApiDecrypt(response) {
    if (response.config.responseType === 'blob') {
        return response;
    }
    checkKey();
    decryptData(response);
}

function checkKey() {
    if (!originKey || !originIv) {
        getEncryptKey();
    }
}

function getEncryptKey() {
    request({
        url: '/common/getEncryptKey',
        method: 'POST'
    }).then(successResponse => {
        const data = successResponse.data.data;
        originKey = data.key;
        originIv = data.iv;
        isUrlSafe = data.isUrlSafe;
    }).catch(() => {
        console.log('请求加解密密钥失败');
    });
}

/**
 * 根据前后端协商的密文密钥 base64 是否是 url 安全的进行密钥转换
 * @return 转换后的密钥
 */
function getKey() {
    let key = originKey;
    let iv = originIv;
    // 如果前后端协商的密文和密钥的 base64 是 url 安全的，需要把 base64 转换为 url 不安全的，因为 CryptoJS 不支持 url 安全的 base64 的处理
    if (isUrlSafe) {
        // 把转换的密钥 base64 保存起来，提高性能
        if (!urlNotSafeKey) {
            urlNotSafeKey = base64ToUrlNotSafe(originKey);
            urlNotSafeIv = base64ToUrlNotSafe(originIv);
        }
        key = urlNotSafeKey;
        iv = urlNotSafeIv;
        return {key, iv};
    }
    return {key, iv};
}

function encryptParams(config) {
    const { key, iv } = getKey();
    // 检查并准备params
    const params = config.params || {};
    console.log('params:', params);
    const encryptParams = {};
    if (typeof params === 'object' && params !== null && !Array.isArray(params)) {
        // 如果是对象，逐个加密
        for (const paramKey in params) {
            if (params.hasOwnProperty(paramKey)) {
                const value = params[paramKey];
                 // 不加密 pageNum 和 pageSize
                if (paramKey === 'pageNum' || paramKey === 'pageSize') {
                    encryptParams[paramKey] = value;
                    continue;
                }
                if (value === null || value === undefined) {
                    encryptParams[paramKey] = null;
                } else if (typeof value === 'object' || Array.isArray(value)) {
                    encryptParams[paramKey] = encrypt(JSON.stringify(value), key, iv);
                } else {
                    encryptParams[paramKey] = encrypt(String(value), key, iv);
                }
            }
        }
        config.params = encryptParams;
    } else if (Array.isArray(params)) {
        // 如果是数组，转为JSON后加密
        config.params = encrypt(JSON.stringify(params), key, iv);
    } else {
        // 如果是基本类型，直接加密
        config.params = encrypt(String(params), key, iv);
    }
    console.log('Encrypted params:', config.params);
}

function encryptFormData(config) {
    const {key, iv} = getKey();
    // 创建一个 FormData 存储加密后的参数
    const encryptFormData = new FormData();
    // 取出请求里的 FormData，转换为对象，便于判断对象内各字段的数据类型
    const formData = config.data || {};
    const obj = {};
    for (const dataKey of formData.keys()) {
        obj[dataKey] = formData.getAll(dataKey).length > 1 ? formData.getAll(dataKey) : formData.get(dataKey);
    }
    // 遍历对象，把字段一个个加密后塞进上面新创建的 FormData
    for (let objKey in obj) {
        // 字段值
        const value = obj[objKey];
        // 即将放入上面新创建的 FormData 的值
        let formDataValue;
        // 文件类型暂不处理，因为 SecureApi 组件还没有处理文件解密的能力
        if (value instanceof Array && value[0] instanceof File) {
            value.forEach(file => {
                encryptFormData.append(objKey, file);
            });
        } else {
            if (value instanceof File) {
                formDataValue = value;
            } else if (value instanceof Object || value instanceof Array || value instanceof Set || value instanceof Map) {
                // 这些类型需要转换为 json 字符串再加密
                formDataValue = encrypt(JSON.stringify(value), key, iv);
            } else {
                // 其余基本类型不能转换为 json 字符串，因为 json 字符串两边会加上双引号，如 int 类型的 1，变为 "1" 后，后端就会序列化失败
                formDataValue = encrypt(value, key, iv);
            }
            encryptFormData.append(objKey, formDataValue);
        }
    }
    // 替换请求里的 data
    config.data = encryptFormData;

}

/**
 * 处理 data 参数加密
 * @param config 请求信息
 */
function encryptData(config) {
    const {key, iv} = getKey();
    // 请求 data 是对象，转换为 json 字符串后再加密，然后替换 config 的 data
    config.data = encrypt(JSON.stringify(config.data), key, iv);
}

function encrypt(data, AES_KEY, IV) {
    const key = Buffer.from(AES_KEY, 'base64');
    const iv = Buffer.from(IV, 'base64');
    const encryptResult = sm4.encrypt(data, key, {iv: iv, mode: 'cbc', padding: 'pkcs#5'});
    // 把16进制字符串转换成二进制数据（Buffer）
    const buffer = Buffer.from(encryptResult, 'hex');
    // 再把 Buffer 转成 Base64
    const base64Str = buffer.toString('base64');
    return isUrlSafe ? base64ToUrlSafe(base64Str) : base64Str;
}

/**
 * 处理 json 返回值解密
 * @param response 响应信息
 */
function decryptData(response) {
    if (response.data) {
        const {key, iv} = getKey();
        const decryptData = decrypt(response.data, key, iv);
        // 返回值是 json 字符串，转换为对象后替换 response 的 data
        response.data = JSON.parse(decryptData);
    }
}


function decrypt(data, AES_KEY, IV) {
    try {
        data = isUrlSafe ? base64ToUrlNotSafe(data) : data;
        // 将 base64 密钥和 IV 转换为 hex 格式
        const key = Buffer.from(AES_KEY, 'base64');
        const iv = Buffer.from(IV, 'base64');
        // 将 base64 数据转换为 hex 格式
        const encryptedData = Buffer.from(data, 'base64');

        // 使用 SM4 解密
        const decryptResult = sm4.decrypt(encryptedData, key, {iv: iv, mode: 'cbc', padding: 'pkcs#5'});

        // 直接尝试用 utf8 解析结果，避免错误转换
        const result = decryptResult.toString();
        return result;
    } catch (error) {
        console.error('SM4 解密失败:', error);
        return null;
    }
}


function base64ToUrlNotSafe(base64Str) {
    if (!base64Str) return '';
    let safeStr = base64Str.replace(/-/g, '+').replace(/_/g, '/');
    let num = safeStr.length % 4;
    return safeStr + '===='.substring(0, num);
}

/**
 * 编码 base64 -> URL Safe base64
 * '+' -> '-'
 * '/' -> '_'
 * '=' -> ''
 * @param base64Str base64字符串
 * @return url 安全的 base64;
 */
function base64ToUrlSafe(base64Str) {
    if (!base64Str) return '';
    //.replace(/=+$/, '')，等号无需处理
    return base64Str.replace(/\+/g, '-').replace(/\//g, '_');
}

export {handleApiEncrypt, handleApiDecrypt};
